using System; 
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Linq; 
using System.Reflection;
using System.Runtime.Versioning; 
using System.Text; 
using System.Threading;
using System.Xml; 
using System.Xml.Serialization;
using LinqToSqlShared.Mapping;

namespace System.Data.Linq.Mapping { 
    using System.Data.Linq.Provider;
    using System.Diagnostics.CodeAnalysis; 
 
    /// <summary>
    /// Represents a source for mapping information. 
    /// </summary>
    public abstract class MappingSource {
        MetaModel primaryModel;
        ReaderWriterLock rwlock; 
        Dictionary<Type, MetaModel> secondaryModels;
 
        /// <summary> 
        /// Gets the MetaModel representing a DataContext and all it's
        /// accessible tables, functions and entities. 
        /// </summary>
        public MetaModel GetModel(Type dataContextType) {
            if (dataContextType == null) {
                throw Error.ArgumentNull("dataContextType"); 
            }
            MetaModel model = null; 
            if (this.primaryModel == null) { 
                model = this.CreateModel(dataContextType);
                Interlocked.CompareExchange<MetaModel>(ref this.primaryModel, model, null); 
            }

            // if the primary one matches, use it!
            if (this.primaryModel.ContextType == dataContextType) { 
                return this.primaryModel;
            } 
 
            // the rest of this only happens if you are using the mapping source for
            // more than one context type 

            // build a map if one is not already defined
            if (this.secondaryModels == null) {
                Interlocked.CompareExchange<Dictionary<Type, MetaModel>>(ref this.secondaryModels, new Dictionary<Type, MetaModel>(), null); 
            }
 
            // if we haven't created a read/writer lock, make one now 
            if (this.rwlock == null) {
                Interlocked.CompareExchange<ReaderWriterLock>(ref this.rwlock, new ReaderWriterLock(), null); 
            }

            // lock the map and look inside
            MetaModel foundModel; 
            this.rwlock.AcquireReaderLock(Timeout.Infinite);
            try { 
                if (this.secondaryModels.TryGetValue(dataContextType, out foundModel)) { 
                    return foundModel;
                } 
            }
            finally {
                this.rwlock.ReleaseReaderLock();
            } 
            // if it wasn't found, lock for write and try again
            this.rwlock.AcquireWriterLock(Timeout.Infinite); 
            try { 
                if (this.secondaryModels.TryGetValue(dataContextType, out foundModel)) {
                    return foundModel; 
                }
                if (model == null) {
                    model = this.CreateModel(dataContextType);
                } 
                this.secondaryModels.Add(dataContextType, model);
            } 
            finally { 
                this.rwlock.ReleaseWriterLock();
            } 
            return model;
        }

        /// <summary> 
        /// Creates a new instance of a MetaModel.  This method is called by GetModel().
        /// Override this method when defining a new type of MappingSource. 
        /// </summary> 
        /// <param name="dataContextType"></param>
        /// <returns></returns> 
        protected abstract MetaModel CreateModel(Type dataContextType);
    }

 
    /// <summary>
    /// A mapping source that uses attributes on the context to create the mapping model. 
    /// </summary> 
    public sealed class AttributeMappingSource : MappingSource {
        public AttributeMappingSource() { 
        }

        protected override MetaModel CreateModel(Type dataContextType) {
            if (dataContextType == null) { 
                throw Error.ArgumentNull("dataContextType");
            } 
            return new AttributedMetaModel(this, dataContextType); 
        }
    } 


    /// <summary>
    /// A mapping source that uses an external XML mapping source to create the model. 
    /// </summary>
    public sealed class XmlMappingSource : MappingSource { 
        DatabaseMapping map; 

        [ResourceExposure(ResourceScope.Assembly)] // map parameter contains type names. 
        private XmlMappingSource(DatabaseMapping map) {
            this.map = map;
        }
 
        [ResourceExposure(ResourceScope.None)] // Exposure is via map instance variable.
        [ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine, ResourceScope.Assembly | ResourceScope.Machine)] // For MappedMetaModel constructor call. 
        protected override MetaModel CreateModel(Type dataContextType) { 
            if (dataContextType == null) {
                throw Error.ArgumentNull("dataContextType"); 
            }
            return new MappedMetaModel(this, dataContextType, this.map);
        }
 
        /// <summary>
        /// Create a mapping source from xml string. 
        /// </summary> 
        /// <param name="dataContextType">The type of DataContext to base the mapping on.</param>
        /// <param name="xml">A string containing XML.</param> 
        /// <returns>The mapping source.</returns>
        [ResourceExposure(ResourceScope.Assembly)] // Xml contains type names.
        [ResourceConsumption(ResourceScope.Assembly)] // For FromReader method call.
        public static XmlMappingSource FromXml(string xml) { 
            if (xml == null) {
                throw Error.ArgumentNull("xml"); 
            } 
            XmlReader reader = new XmlTextReader(new System.IO.StringReader(xml));
            return FromReader(reader); 
        }

        /// <summary>
        /// Create a mapping source from xml reader. 
        /// </summary>
        /// <param name="dataContextType">The type of DataContext to base the mapping on.</param> 
        /// <param name="xml">An xml reader.</param> 
        /// <returns>The mapping source.</returns>
        [ResourceExposure(ResourceScope.Assembly)] // reader parameter contains type names. 
        [ResourceConsumption(ResourceScope.Assembly)] // XmlMappingSource constructor call.
        public static XmlMappingSource FromReader(XmlReader reader) {
            if (reader == null) {
                throw Error.ArgumentNull("reader"); 
            }
            reader.MoveToContent(); 
            DatabaseMapping db = XmlMappingReader.ReadDatabaseMapping(reader); 

            if (db == null) { 
                throw Error.DatabaseNodeNotFound(XmlMappingConstant.MappingNamespace);
            }

            return new XmlMappingSource(db); 
        }
 
        /// <summary> 
        /// Create a mapping source from xml in a stream.
        /// </summary> 
        /// <param name="dataContextType">The type of DataContext to base the mapping on.</param>
        /// <param name="xml">A stream of xml.</param>
        /// <returns>The mapping source.</returns>
        [ResourceExposure(ResourceScope.Assembly)] // Stream contains type names. 
        [ResourceConsumption(ResourceScope.Assembly)] // For FromReader method call.
        public static XmlMappingSource FromStream(System.IO.Stream stream) { 
            if (stream == null) { 
                throw Error.ArgumentNull("stream");
            } 
            XmlReader reader = new XmlTextReader(stream);
            return FromReader(reader);
        }
 
        /// <summary>
        /// Create a mapping source from xml loaded from a url. 
        /// </summary> 
        /// <param name="dataContextType">The type of DataContext to base the mapping on.</param>
        /// <param name="url">The Url pointing to the xml.</param> 
        /// <returns>The mapping source.</returns>
        [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#", Justification="Unknown reason.")]
        [ResourceExposure(ResourceScope.Machine | ResourceScope.Assembly)] // url parameter which may contain type names.
        [ResourceConsumption(ResourceScope.Machine | ResourceScope.Assembly)] // XmlTextReader constructor & FromReader method call. 
        public static XmlMappingSource FromUrl(string url) {
            if (url == null) { 
                throw Error.ArgumentNull("url"); 
            }
            XmlReader reader = new XmlTextReader(url); 
            try {
                return FromReader(reader);
            }
            finally { 
                reader.Close();
            } 
        } 
    }
 
    class XmlMappingReader {
        private static string RequiredAttribute(XmlReader reader, string attribute) {
            string result = OptionalAttribute(reader, attribute);
            if (result == null) { 
                throw Error.CouldNotFindRequiredAttribute(attribute, reader.ReadOuterXml());
            } 
            return result; 
        }
 
        private static string OptionalAttribute(XmlReader reader, string attribute) {
            return reader.GetAttribute(attribute);
        }
 
        private static bool OptionalBoolAttribute(XmlReader reader, string attribute, bool @default) {
            string value = OptionalAttribute(reader, attribute); 
            return (value != null) ? bool.Parse(value) : @default; 
        }
 
        private static bool? OptionalNullableBoolAttribute(XmlReader reader, string attribute) {
            string value = OptionalAttribute(reader, attribute);
            return (value != null) ? (bool?)bool.Parse(value) : null;
        } 

        private static void AssertEmptyElement(XmlReader reader) { 
            if (!reader.IsEmptyElement) { 
                string nodeName = reader.Name;
                reader.Read(); 
                if (reader.NodeType != XmlNodeType.EndElement) {
                    throw Error.ExpectedEmptyElement(nodeName, reader.NodeType, reader.Name);
                }
            } 

            reader.Skip(); 
        } 

        internal static DatabaseMapping ReadDatabaseMapping(XmlReader reader) { 
            System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element);
            if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.Database) {
                return null;
            } 

            ValidateAttributes(reader, new[] { 
                                               XmlMappingConstant.Name, 
                                               XmlMappingConstant.Provider
                                           }); 

            DatabaseMapping dm = new DatabaseMapping();

            dm.DatabaseName = RequiredAttribute(reader, XmlMappingConstant.Name); 
            dm.Provider = OptionalAttribute(reader, XmlMappingConstant.Provider);
 
            if (!reader.IsEmptyElement) { 
                reader.ReadStartElement();
                reader.MoveToContent(); 
                while (reader.NodeType != XmlNodeType.EndElement) {
                    if (reader.NodeType == XmlNodeType.Whitespace || !IsInNamespace(reader)) {
                        reader.Skip();
                        continue; 
                    }
 
                    switch (reader.LocalName) { 
                        case XmlMappingConstant.Table:
                            dm.Tables.Add(ReadTableMapping(reader)); 
                            break;
                        case XmlMappingConstant.Function:
                            dm.Functions.Add(ReadFunctionMapping(reader));
                            break; 
                        default:
                            throw Error.UnrecognizedElement(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName)); 
                    } 
                    reader.MoveToContent();
                } 

                if (reader.LocalName != XmlMappingConstant.Database) {
                    throw Error.UnexpectedElement(XmlMappingConstant.Database, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
                } 

                reader.ReadEndElement(); 
            } 
            else {
                System.Diagnostics.Debug.Assert(false, "DatabaseMapping has no content"); 
                reader.Skip();
            }

            return dm; 
        }
 
        internal static bool IsInNamespace(XmlReader reader) { 
            return reader.LookupNamespace(reader.Prefix) == XmlMappingConstant.MappingNamespace;
        } 

        internal static void ValidateAttributes(XmlReader reader, string[] validAttributes) {
            if (reader.HasAttributes) {
                List<string> attrList = new List<string>(validAttributes); 
                const string xmlns = "xmlns";
 
                for (int i = 0; i < reader.AttributeCount; i++) { 
                    reader.MoveToAttribute(i);
 
                    // if the node's in the namespace, it is required to be one of the valid ones
                    if (IsInNamespace(reader) && reader.LocalName != xmlns && !attrList.Contains(reader.LocalName)) {
                        throw Error.UnrecognizedAttribute(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : ":", reader.LocalName));
                    } 
                }
 
                reader.MoveToElement(); // Moves the reader back to the element node. 
            }
        } 

        internal static FunctionMapping ReadFunctionMapping(XmlReader reader) {
            System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element);
            if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.Function) { 
                throw Error.UnexpectedElement(XmlMappingConstant.Function, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
            } 
 
            ValidateAttributes(reader, new[] {
                                               XmlMappingConstant.Name, 
                                               XmlMappingConstant.Method,
                                               XmlMappingConstant.IsComposable
                                           });
 
            FunctionMapping fm = new FunctionMapping();
            fm.MethodName = RequiredAttribute(reader, XmlMappingConstant.Method); 
            fm.Name = OptionalAttribute(reader, XmlMappingConstant.Name); 
            fm.IsComposable = OptionalBoolAttribute(reader, XmlMappingConstant.IsComposable, false);
 
            if (!reader.IsEmptyElement) {
                reader.ReadStartElement();
                reader.MoveToContent();
                while (reader.NodeType != XmlNodeType.EndElement) { 
                    if (reader.NodeType == XmlNodeType.Whitespace || !IsInNamespace(reader)) {
                        reader.Skip(); 
                        continue; 
                    }
 
                    switch (reader.LocalName) {
                        case XmlMappingConstant.Parameter:
                            fm.Parameters.Add(ReadParameterMapping(reader));
                            break; 
                        case XmlMappingConstant.ElementType:
                            fm.Types.Add(ReadElementTypeMapping(null, reader)); 
                            break; 
                        case XmlMappingConstant.Return:
                            fm.FunReturn = ReadReturnMapping(reader); 
                            break;
                        default:
                            throw Error.UnrecognizedElement(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
                    } 
                    reader.MoveToContent();
                } 
                reader.ReadEndElement(); 
            }
            else { 
                // no content is okay
                reader.Skip();
            }
 
            return fm;
        } 
 
        private static ReturnMapping ReadReturnMapping(XmlReader reader) {
            System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element); 
            if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.Return) {
                throw Error.UnexpectedElement(XmlMappingConstant.Return, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
            }
 
            ValidateAttributes(reader, new[] {
                                               XmlMappingConstant.DbType 
                                           }); 

            ReturnMapping rm = new ReturnMapping(); 
            rm.DbType = OptionalAttribute(reader, XmlMappingConstant.DbType);

            AssertEmptyElement(reader);
 
            return rm;
        } 
 
        private static ParameterMapping ReadParameterMapping(XmlReader reader) {
            System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element); 
            if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.Parameter) {
                throw Error.UnexpectedElement(XmlMappingConstant.Parameter, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
            }
 
            ValidateAttributes(reader, new[] {
                                               XmlMappingConstant.Name, 
                                               XmlMappingConstant.DbType, 
                                               XmlMappingConstant.Parameter,
                                               XmlMappingConstant.Direction 
                                           });

            ParameterMapping pm = new ParameterMapping();
            pm.Name = RequiredAttribute(reader, XmlMappingConstant.Name); 
            pm.ParameterName = RequiredAttribute(reader, XmlMappingConstant.Parameter);
            pm.DbType = OptionalAttribute(reader, XmlMappingConstant.DbType); 
            pm.XmlDirection = OptionalAttribute(reader, XmlMappingConstant.Direction); 

            AssertEmptyElement(reader); 

            return pm;
        }
 
        private static TableMapping ReadTableMapping(XmlReader reader) {
            System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element); 
            if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.Table) { 
                throw Error.UnexpectedElement(XmlMappingConstant.Table, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
            } 

            ValidateAttributes(reader, new[] {
                                               XmlMappingConstant.Name,
                                               XmlMappingConstant.Member 
                                           });
 
            TableMapping tm = new TableMapping(); 
            tm.TableName = OptionalAttribute(reader, XmlMappingConstant.Name);
            tm.Member = OptionalAttribute(reader, XmlMappingConstant.Member); 

            if (!reader.IsEmptyElement) {
                reader.ReadStartElement();
                reader.MoveToContent(); 
                while (reader.NodeType != XmlNodeType.EndElement) {
                    if (reader.NodeType == XmlNodeType.Whitespace || !IsInNamespace(reader)) { 
                        reader.Skip(); 
                        continue;
                    } 

                    switch (reader.LocalName) {
                        case XmlMappingConstant.Type:
                            if (tm.RowType != null) { 
                                goto default;
                            } 
                            tm.RowType = ReadTypeMapping(null, reader); 
                            break;
                        default: 
                            throw Error.UnrecognizedElement(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
                    }
                    reader.MoveToContent();
                } 

                if (reader.LocalName != XmlMappingConstant.Table) { 
                    throw Error.UnexpectedElement(XmlMappingConstant.Table, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName)); 
                }
 
                reader.ReadEndElement();
            }
            else {
                System.Diagnostics.Debug.Assert(false, "Table has no content"); 
                reader.Skip();
            } 
 
            return tm;
        } 

        private static TypeMapping ReadElementTypeMapping(TypeMapping baseType, XmlReader reader) {
            System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element);
            if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.ElementType) { 
                throw Error.UnexpectedElement(XmlMappingConstant.Type, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
            } 
 
            return ReadTypeMappingImpl(baseType, reader);
        } 

        private static TypeMapping ReadTypeMapping(TypeMapping baseType, XmlReader reader) {
            System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element);
            if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.Type) { 
                throw Error.UnexpectedElement(XmlMappingConstant.Type, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
            } 
 
            return ReadTypeMappingImpl(baseType, reader);
        } 

        private static TypeMapping ReadTypeMappingImpl(TypeMapping baseType, XmlReader reader) {
            ValidateAttributes(reader, new[] {
                                                 XmlMappingConstant.Name, 
                                                 XmlMappingConstant.InheritanceCode,
                                                 XmlMappingConstant.IsInheritanceDefault 
                                             }); 

            TypeMapping tm = new TypeMapping(); 
            tm.BaseType = baseType;
            tm.Name = RequiredAttribute(reader, XmlMappingConstant.Name);
            tm.InheritanceCode = OptionalAttribute(reader, XmlMappingConstant.InheritanceCode);
            tm.IsInheritanceDefault = OptionalBoolAttribute(reader, XmlMappingConstant.IsInheritanceDefault, false); 

            if (!reader.IsEmptyElement) { 
                reader.ReadStartElement(); 
                reader.MoveToContent();
                while (reader.NodeType != XmlNodeType.EndElement) { 
                    if (reader.NodeType == XmlNodeType.Whitespace || !IsInNamespace(reader)) {
                        reader.Skip();
                        continue;
                    } 

                    switch (reader.LocalName) { 
                        case XmlMappingConstant.Type: 
                            tm.DerivedTypes.Add(ReadTypeMapping(tm, reader));
                            break; 
                        case XmlMappingConstant.Association:
                            tm.Members.Add(ReadAssociationMapping(reader));
                            break;
                        case XmlMappingConstant.Column: 
                            tm.Members.Add(ReadColumnMapping(reader));
                            break; 
                        default: 
                            throw Error.UnrecognizedElement(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
                    } 
                    reader.MoveToContent();
                }
                reader.ReadEndElement();
            } 
            else {
                // no content is okay 
                reader.Skip(); 
            }
            return tm; 
        }

        private static AssociationMapping ReadAssociationMapping(XmlReader reader) {
            System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element); 
            if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.Association) {
                throw Error.UnexpectedElement(XmlMappingConstant.Association, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName)); 
            } 

            ValidateAttributes(reader, new[] { 
                                               XmlMappingConstant.Name,
                                               XmlMappingConstant.IsForeignKey,
                                               XmlMappingConstant.IsUnique,
                                               XmlMappingConstant.Member, 
                                               XmlMappingConstant.OtherKey,
                                               XmlMappingConstant.Storage, 
                                               XmlMappingConstant.ThisKey, 
                                               XmlMappingConstant.DeleteRule,
                                               XmlMappingConstant.DeleteOnNull, 
                                           });

            AssociationMapping am = new AssociationMapping();
            am.DbName = OptionalAttribute(reader, XmlMappingConstant.Name); 
            am.IsForeignKey = OptionalBoolAttribute(reader, XmlMappingConstant.IsForeignKey, false);
            am.IsUnique = OptionalBoolAttribute(reader, XmlMappingConstant.IsUnique, false); 
            am.MemberName = RequiredAttribute(reader, XmlMappingConstant.Member); 
            am.OtherKey = OptionalAttribute(reader, XmlMappingConstant.OtherKey);
            am.StorageMemberName = OptionalAttribute(reader, XmlMappingConstant.Storage); 
            am.ThisKey = OptionalAttribute(reader, XmlMappingConstant.ThisKey);
            am.DeleteRule = OptionalAttribute(reader, XmlMappingConstant.DeleteRule);
            am.DeleteOnNull = OptionalBoolAttribute(reader, XmlMappingConstant.DeleteOnNull, false);
 
            AssertEmptyElement(reader);
 
            return am; 
        }
 
        private static ColumnMapping ReadColumnMapping(XmlReader reader) {
            System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element);
            if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.Column) {
                throw Error.UnexpectedElement(XmlMappingConstant.Column, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName)); 
            }
 
            ValidateAttributes(reader, new[] { 
                                               XmlMappingConstant.Name,
                                               XmlMappingConstant.DbType, 
                                               XmlMappingConstant.IsDbGenerated,
                                               XmlMappingConstant.IsDiscriminator,
                                               XmlMappingConstant.IsPrimaryKey,
                                               XmlMappingConstant.IsVersion, 
                                               XmlMappingConstant.Member,
                                               XmlMappingConstant.Storage, 
                                               XmlMappingConstant.Expression, 
                                               XmlMappingConstant.CanBeNull,
                                               XmlMappingConstant.UpdateCheck, 
                                               XmlMappingConstant.AutoSync
                                           });

            ColumnMapping cm = new ColumnMapping(); 
            cm.DbName = OptionalAttribute(reader, XmlMappingConstant.Name);
            cm.DbType = OptionalAttribute(reader, XmlMappingConstant.DbType); 
            cm.IsDbGenerated = OptionalBoolAttribute(reader, XmlMappingConstant.IsDbGenerated, false); 
            cm.IsDiscriminator = OptionalBoolAttribute(reader, XmlMappingConstant.IsDiscriminator, false);
            cm.IsPrimaryKey = OptionalBoolAttribute(reader, XmlMappingConstant.IsPrimaryKey, false); 
            cm.IsVersion = OptionalBoolAttribute(reader, XmlMappingConstant.IsVersion, false);
            cm.MemberName = RequiredAttribute(reader, XmlMappingConstant.Member);
            cm.StorageMemberName = OptionalAttribute(reader, XmlMappingConstant.Storage);
            cm.Expression = OptionalAttribute(reader, XmlMappingConstant.Expression); 
            cm.CanBeNull = OptionalNullableBoolAttribute(reader, XmlMappingConstant.CanBeNull);
            string updateCheck = OptionalAttribute(reader, XmlMappingConstant.UpdateCheck); 
            cm.UpdateCheck = (updateCheck == null) ? UpdateCheck.Always : (UpdateCheck)Enum.Parse(typeof(UpdateCheck), updateCheck); 
            string autoSync = OptionalAttribute(reader, XmlMappingConstant.AutoSync);
            cm.AutoSync = (autoSync == null) ? AutoSync.Default : (AutoSync)Enum.Parse(typeof(AutoSync), autoSync); 

            AssertEmptyElement(reader);

            return cm; 
        }
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.